<?php

namespace Henan\ThinkSdk\utils;

use Exception;

/**
 * Curl工具类
 */
class CurlUtil
{
    public int $jsonDecodeOptions = 0; // json解码选项
    public array $headers = []; // 请求头
    public string $proxyIp = ''; // 代理IP
    public string $proxyPort = '3128'; // 代理端口
    public int $statusCode = 0; // 状态码
    public string $error = ''; // 错误信息
    public int $timeout = 30; // 请求超时时间 (秒)
    public int $connectTimeout = 30; // 连接超时时间 (秒)

    /**
     * 设置Cookie
     * @param string|array $cookie
     * @return CurlUtil
     */
    public function setCookie(string|array $cookie): self
    {
        if (is_array($cookie)) {
            foreach ($cookie as $key => $value) $this->headers['Cookie'][] = "$key=$value";
            $this->headers['Cookie'] = implode('; ', $this->headers['Cookie']);
        } else {
            $this->headers['Cookie'] = $cookie;
        }
        return $this;
    }

    /**
     * 设置代理
     * @param string $proxyIp
     * @param string $proxyPort
     * @return CurlUtil
     */
    public function setProxy(string $proxyIp, string $proxyPort): self
    {
        $this->proxyIp = $proxyIp;
        $this->proxyPort = $proxyPort;
        return $this;
    }

    /**
     * 设置随机User-Agent
     * @return CurlUtil
     */
    public function setRandomUserAgent(): self
    {
        $userAgents = [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
            "Mozilla/5.0 (iPad; CPU OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0",
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.3",
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.3",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0."
        ];
        $this->headers['User-Agent'] = $userAgents[array_rand($userAgents)];
        return $this;
    }

    /**
     * 设置自定义SSL证书验证
     * @param bool $verifyPeer 是否验证对端证书
     * @param bool $verifyHost 是否验证证书的主机名
     * @return CurlUtil
     */
    public function setSSLVerification(bool $verifyPeer = false, bool $verifyHost = false): self
    {
        $this->headers['SSL_VERIFY_PEER'] = $verifyPeer;
        $this->headers['SSL_VERIFY_HOST'] = $verifyHost;
        return $this;
    }

    /**
     * 设置请求头
     * @param array $headers
     * @return CurlUtil
     */
    public function setHeaders(array $headers): self
    {
        $this->headers = array_merge($this->headers, $headers);
        return $this;
    }

    /**
     * 设置请求超时
     * @param int $timeout 请求超时（秒）
     * @param int $connectTimeout 连接超时（秒）
     * @return CurlUtil
     */
    public function setTimeout(int $timeout, int $connectTimeout = 30): self
    {
        $this->timeout = $timeout;
        $this->connectTimeout = $connectTimeout;
        return $this;
    }

    /**
     * 请求数据
     * @param string $url
     * @param array $data
     * @param string $method
     * @return array
     * @throws Exception
     */
    public function request(string $url, array $data = [], string $method = 'GET'): array
    {
        // 初始化cURL会话
        $ch = curl_init();
        // 设置cURL选项
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method)); // 请求方式
        if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
            $this->headers['Content-Type'] = 'application/json'; // 请求头 内容类型
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, $this->jsonDecodeOptions)); // 请求参数
        } else {
            $url .= (!str_contains($url, '?') ? '?' : '&') . http_build_query($data);
        }
        curl_setopt($ch, CURLOPT_URL, $url); // 目标URL
        // 设置代理
        if ($this->proxyIp) {
            curl_setopt($ch, CURLOPT_PROXY, $this->proxyIp); // 代理IP
            curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxyPort); // 代理端口
        }
        // 设置请求头
        $heads = [];
        foreach ($this->headers as $key => $item) $heads[] = "$key:$item";
        curl_setopt($ch, CURLOPT_HTTPHEADER, $heads);
        // 设置SSL证书验证
        if (isset($this->headers['SSL_VERIFY_PEER'])) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $this->headers['SSL_VERIFY_PEER']);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $this->headers['SSL_VERIFY_HOST']);
        } else {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
        // 返回内容，而非输出
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 将获取的信息以文件流的形式返回，而不是直接输出
        curl_setopt($ch, CURLOPT_HEADER, false); // 不需要返回头部信息
        // 执行请求
        $response = curl_exec($ch);
        // 错误处理
        if (curl_errno($ch)) {
            $this->error = 'cURL错误：' . curl_error($ch);
            curl_close($ch);
            throw new Exception($this->error);
        }
        $this->statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        // 返回响应数据
        if ($response === '') {
            throw new Exception('Empty response from server');
        }
        // 尝试JSON解码
        $result = json_decode($response, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception('Invalid JSON response: ' . json_last_error_msg());
        }
        return $result;
    }

    /**
     * 发送GET请求
     * @param string $url
     * @param array $data
     * @return array
     * @throws Exception
     */
    public function get(string $url, array $data = []): array
    {
        return $this->request($url, $data);
    }

    /**
     * 发送POST请求
     * @param string $url
     * @param array $data
     * @return array
     * @throws Exception
     */
    public function post(string $url, array $data = []): array
    {
        return $this->request($url, $data, 'POST');
    }

    /**
     * 发送PUT请求
     * @param string $url
     * @param array $data
     * @return array
     * @throws Exception
     */
    public function put(string $url, array $data = []): array
    {
        return $this->request($url, $data, 'PUT');
    }

    /**
     * 发送PATCH请求
     * @param string $url
     * @param array $data
     * @return array
     * @throws Exception
     */
    public function patch(string $url, array $data = []): array
    {
        return $this->request($url, $data, 'PATCH');
    }

    /**
     * 发送DELETE请求
     * @param string $url
     * @param array $data
     * @return array
     * @throws Exception
     */
    public function delete(string $url, array $data = []): array
    {
        return $this->request($url, $data, 'DELETE');
    }
}